#include "cell.h"

Cell::Cell()
{
}

Cell::Cell(Cell *cell)
{
    mCachedValue = cell->mCachedValue;
    mCachedIsDirty = cell->mCachedIsDirty;
}

QTableWidgetItem* Cell::clone() const
{
//    QTableWidgetItem* a(new Cell(*this));
//    return a;
    return new Cell(*this);
}

void Cell::setData(int role, const QVariant &value)
{
    //call father class method to restore role and value.
    QTableWidgetItem::setData(role, value);

    //ture if the role is editrole, indicate that the cell is modified.
    if (Qt::EditRole == role) {
        setDirty();
    }
}

QVariant Cell::data(int role) const
{
    if (Qt::DisplayRole == role) {
        if ( value().isValid()) {
            return value().toString();
        } else {
            return "####";
        }
    } else if (Qt::TextAlignmentRole == role) {
        if (QVariant::String == value().type()) {
            return (int)(Qt::AlignLeft | Qt::AlignVCenter);
        } else {
            return (int)(Qt::AlignRight | Qt::AlignVCenter);
        }
    } else {
        return QTableWidgetItem::data(role);
    }
}

void Cell::setFormula(const QString &formula)
{
    setData(Qt::EditRole, formula);
}

QString Cell::formula() const
{
    return data(Qt::EditRole).toString();
}

void Cell::setDirty()
{
    mCachedIsDirty = true;
}

QVariant Invalid;

QVariant Cell::value() const
{
    if (mCachedIsDirty) {
        mCachedIsDirty = false;

        QString formulaStr = formula();

        //true
        //copy the rest of string to mCachedValue, if the string is bengin of '/'.
        if (formulaStr.startsWith('\'')) {
            mCachedValue = formulaStr.mid(1);
        } else if (formulaStr.startsWith('=')) {

            mCachedValue = Invalid;
            QString expr(formulaStr.mid(1));
            //cutoff " "  by "".
            expr.replace(" ", "");
            expr.append(QChar::Null);

            int pos = 0;

            //计算得出值。
            mCachedValue = evalExpression(expr, pos);

            //true 字符串处理异常，将无效值赋予。
            if (expr[pos] != QChar::Null) {
                mCachedValue = Invalid;
            }
          //字符串的其他形式，并处理。
        } else {
            bool ok;
            double d(formulaStr.toDouble(&ok));

            //true 字符串转为为double数据成功。
            if (ok) {
                mCachedValue = d;
            } else {
                mCachedValue = formulaStr;
            }
        }
    }

    return mCachedValue;
}

QVariant Cell::evalExpression(const QString &str, int &pos) const
{
    QVariant result = evalTerm(str, pos);
    while (str[pos] != QChar::Null) {
        QChar op = str[pos];
        if (op != '+' && op != '-')
            return result;
        ++pos;

        QVariant term = evalTerm(str, pos);
        if (result.type() == QVariant::Double
                && term.type() == QVariant::Double) {
            if (op == '+') {
                result = result.toDouble() + term.toDouble();
            } else {
                result = result.toDouble() - term.toDouble();
            }
        } else {
            result = Invalid;
        }
    }

    return result;
}

QVariant Cell::evalTerm(const QString &str, int &pos) const
{
    QVariant result = evalFactor(str, pos);
    while (str[pos] != QChar::Null) {
        QChar op = str[pos];
        if (op != '*' && op != '/')
            return result;
        ++pos;

        QVariant factor = evalFactor(str, pos);
        if (result.type() == QVariant::Double
                && factor.type() == QVariant::Double) {
            if (op == '*') {
                result = result.toDouble() * factor.toDouble();
            } else {
                if (factor.toDouble() == 0.0) {
                    result = Invalid;
                } else {
                    result = result.toDouble() / factor.toDouble();
                }
            }
        } else {
            result = Invalid;
        }
    }
    return result;
}

QVariant Cell::evalFactor(const QString &str, int &pos) const
{
    QVariant result;
    bool negative = false;

    if (str[pos] == '-') {
        negative = true;
        ++pos;
    }

    if (str[pos] == '(') {
        ++pos;
        result = evalExpression(str, pos);
        if (str[pos] != ')')
            result = Invalid;
        ++pos;
    } else {
        QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");
        QString token;

        while (str[pos].isLetterOrNumber() || str[pos] == '.') {
            token += str[pos];
            ++pos;
        }

        if (regExp.exactMatch(token)) {
            int column = token[0].toUpper().unicode() - 'A';
            int row = token.mid(1).toInt() - 1;

            Cell *c = static_cast<Cell *>(
                              tableWidget()->item(row, column));
            if (c) {
                result = c->value();
            } else {
                result = 0.0;
            }
        } else {
            bool ok;
            result = token.toDouble(&ok);
            if (!ok)
                result = Invalid;
        }
    }

    if (negative) {
        if (result.type() == QVariant::Double) {
            result = -result.toDouble();
        } else {
            result = Invalid;
        }
    }
    return result;
}
